-
Notifications
You must be signed in to change notification settings - Fork 362
KTOR-9165 and KTOR-9193 Documentation for OpenAPI specification generation #744
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 3.4.0
Are you sure you want to change the base?
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
3c9da2d to
ef10e78
Compare
| }.annotate { | ||
| summary = "Get users" | ||
| description = "Retrieves a list of users." | ||
| parameters { | ||
| query("q") { | ||
| description = "An encoded query" | ||
| required = false | ||
| } | ||
| } | ||
| responses { | ||
| HttpStatusCode.OK { | ||
| description = "A list of users" | ||
| schema = jsonSchema<List<User>>() | ||
| } | ||
| HttpStatusCode.BadRequest { | ||
| description = "Invalid query" | ||
| ContentType.Text.Plain() | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good to leave a comment here that describes the order of precedence here in building the final model. In this case, we have the comment API and code inference that will supply some of the details, which will merged with the runtime API call from the annotate function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a short paragraph below the example and a new section called "Metadata precedence". Not sure if the placement of it makes sense though and if it's better to place it higher up 🤔
topics/openapi-spec-generation.md
Outdated
| #### Supported KDoc fields | ||
|
|
||
| | Tag | Format | Description | | ||
| |-----------------|-------------------------------------------------|----------------------------------| | ||
| | `@tag` | `@tag *name` | Groups the endpoint under a tag | | ||
| | `@path` | `@path [Type] name description` | Path parameter | | ||
| | `@query` | `@query [Type] name description` | Query parameter | | ||
| | `@header` | `@header [Type] name description` | Header parameter | | ||
| | `@cookie` | `@cookie [Type] name description` | Cookie parameter | | ||
| | `@body` | `@body contentType [Type] description` | Request body | | ||
| | `@response` | `@response code contentType [Type] description` | Response definition | | ||
| | `@deprecated` | `@deprecated reason` | Marks the endpoint as deprecated | | ||
| | `@description` | `@description text` | Extended description | | ||
| | `@security` | `@security scheme` | Security requirements | | ||
| | `@externalDocs` | `@external href` | External documentation link | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For 3.4.0, we're updating the comment API style from KDoc tags to use a custom markdown format, due to some concerns over the tags potentially causing some unexpected behaviour in the IDE and KDoc processing. I just changed this last week after some comments from @whyoleg, so it's pretty last-minute 😅
So now all these keywords instead just need to be at the start of the line with a colon divider to designate the value:
/**
* Get a list of widgets.
*
* - Tags:
* - widgets
* - admin
* - Cookie: X-Analytics-Session some tracking cookie
* - Responses:
* - 200 application/json [Widget]+ A list of widgets
* - 400 Invalid request
*/
Note, the plural form with tags and responses is an optional format, you can also use Tag: widgets, and the list bullets at the top level are also optional and just a matter of preference for formatting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the tip. I've updated the section to reflect the changes.
| val docs = generateOpenApiDoc( | ||
| OpenApiDoc(info = OpenApiInfo("My API", "1.0")), | ||
| apiRoute.descendants() | ||
| ) | ||
| call.respond(docs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth noting that docs is a io.ktor.openapi.OpenApiDoc and the return here is assuming you're using ContentNegotiation with kotlinx-serialization and JSON. Alternatively, you could just use Json.encodeToString(docs) with ContentType.Application.Json here with respondText so it's explicit in how it's serializing the model.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some clarification and a note with an alternative code sample using Json.encodeToString(docs) with ContentType.Application.Json.
topics/openapi-spec-generation.md
Outdated
| To expose the OpenAPI specification through an interactive UI, use the [OpenAPI](server-openapi.md) | ||
| and [SwaggerUI](server-swagger-ui.md) plugins. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we have good documentation for the difference between the two plugins. I don't really know the full extent of the differences, but from what I can tell the main difference is that the OpenAPI renders the HTML from the provided model on the back end, and SwaggerUI renders the details from the front end and exposes the model endpoint as JSON or YAML. I would say the Swagger plugin ought to be the default implementation since it's a more modern UI and it's easier to navigate. Maybe @e5l could have some thoughts on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a couple of bullet points to clarify the difference between the two and updated the plugin topics.
|
|
||
| ### Runtime route annotations | ||
|
|
||
| Ktor 3.4.0 introduces the `ktor-server-routing-annotate` module, which allows you to attach OpenAPI metadata directly |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking of changing the name of this module before the release to ktor-server-openapi-annotate - I'd originally used routing because I was thinking of a more generic model and only providing the routing function in here, but since then it's become more OpenAPI-specific and provides the means for top-level information as well.
c3130ed to
78a1528
Compare
397e4a9 to
320c30d
Compare
| } | ||
| } | ||
| ``` | ||
| > For a complete list of available fields, refer to [the OpenAPI Specification](https://swagger.io/specification/#operation-object). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| > For a complete list of available fields, refer to [the OpenAPI Specification](https://swagger.io/specification/#operation-object). | |
| > For a complete list of available fields, refer to the [OpenAPI specification](https://swagger.io/specification/#operation-object). | |
| > | |
| {style="tip"} |
| Ktor allows you to serve OpenAPI documentation based on an OpenAPI specification. | ||
|
|
||
| You can provide the OpenAPI specification in one of the following ways: | ||
| - [Serve an existing YAML or JSON file](#static-openapi-file). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| - [Serve an existing YAML or JSON file](#static-openapi-file). | |
| * [Serve an existing YAML or JSON file](#static-openapi-file). |
|
|
||
| You can provide the OpenAPI specification in one of the following ways: | ||
| - [Serve an existing YAML or JSON file](#static-openapi-file). | ||
| - [Generate the specification at runtime using the OpenAPI compiler extension and runtime APIs](#generate-runtime-openapi-metadata). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| - [Generate the specification at runtime using the OpenAPI compiler extension and runtime APIs](#generate-runtime-openapi-metadata). | |
| * [Generate the specification at runtime using the OpenAPI compiler extension and runtime APIs](#generate-runtime-openapi-metadata). |
| With this, you can access the generated OpenAPI documentation at the `/openapi` path, reflecting the current state of the | ||
| application. | ||
|
|
||
| > For more information on the OpenAPI compiler extension and runtime APIs, see [](openapi-spec-generation.md). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| > For more information on the OpenAPI compiler extension and runtime APIs, see [](openapi-spec-generation.md). | |
| > For more information on the OpenAPI compiler extension and runtime APIs, see [](openapi-spec-generation.md). | |
| > | |
| {style="tip"} |
| ## Configure OpenAPI {id="configure-openapi"} | ||
|
|
||
| By default, documentation is rendered using `StaticHtml2Codegen`. You can customize the renderer inside the `openAPI() { }` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| By default, documentation is rendered using `StaticHtml2Codegen`. You can customize the renderer inside the `openAPI() { }` | |
| By default, documentation is rendered using `StaticHtml2Codegen`. You can customize the renderer inside the `openAPI {}` |
| regular builds, and changes to routes or annotations are reflected in the running server without requiring any | ||
| additional generation steps. | ||
|
|
||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
|
||
| Global OpenAPI metadata is now defined and resolved at runtime rather than during compilation. | ||
|
|
||
| The compiler extension configuration is now limited to feature flags that control how metadata is inferred and collected. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| The compiler extension configuration is now limited to feature flags that control how metadata is inferred and collected. | |
| The compiler extension configuration is now limited to feature options that control how metadata is inferred and collected. |
| #### Configuration | ||
|
|
||
| Configuration is still done using the `openApi {}` block inside the `ktor` Gradle extension. However, properties used | ||
| to define global OpenAPI metadata such as `title`, `version`, `description`, and `target` have been deprecated and are |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| to define global OpenAPI metadata such as `title`, `version`, `description`, and `target` have been deprecated and are | |
| to define global OpenAPI metadata, such as `title`, `version`, `description`, and `target`, have been deprecated and are |
| } | ||
| ``` | ||
|
|
||
| This API can be used as a standalone extension or in conjunction with Ktor's OpenAPI compiler plugin for the automatic |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| This API can be used as a standalone extension or in conjunction with Ktor's OpenAPI compiler plugin for the automatic | |
| You can use this API as a standalone extension or in combination with Ktor's OpenAPI compiler plugin to automatically |
| ``` | ||
|
|
||
| This API can be used as a standalone extension or in conjunction with Ktor's OpenAPI compiler plugin for the automatic | ||
| generation of these calls. The [OpenAPI](server-openapi.md) and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| generation of these calls. The [OpenAPI](server-openapi.md) and | |
| generate these calls. The [OpenAPI](server-openapi.md) and |
| Ktor provides support for building OpenAPI specifications at runtime from one or more documentation sources. | ||
|
|
||
| This functionality is available through: | ||
| - The OpenAPI compiler extension (included in the Ktor Gradle plugin), which analyzes routing code at compile time and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| - The OpenAPI compiler extension (included in the Ktor Gradle plugin), which analyzes routing code at compile time and | |
| * The OpenAPI compiler extension (included in the Ktor Gradle plugin), which analyzes routing code at compile time and |
| This functionality is available through: | ||
| - The OpenAPI compiler extension (included in the Ktor Gradle plugin), which analyzes routing code at compile time and | ||
| generates Kotlin code that registers OpenAPI metadata at runtime. | ||
| - The routing annotation runtime API, which attaches OpenAPI metadata directly to routes in the running application. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| - The routing annotation runtime API, which attaches OpenAPI metadata directly to routes in the running application. | |
| * The routing annotation runtime API, which attaches OpenAPI metadata directly to routes in the running application. |
| generates Kotlin code that registers OpenAPI metadata at runtime. | ||
| - The routing annotation runtime API, which attaches OpenAPI metadata directly to routes in the running application. | ||
|
|
||
| You can use one, or both, and combine them with the [OpenAPI](server-openapi.md) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| You can use one, or both, and combine them with the [OpenAPI](server-openapi.md) | |
| You can use one or both and combine them with the [OpenAPI](server-openapi.md) |
| ``` | ||
|
|
||
| ## Configure the extension | ||
| * To use runtime route annotations, add the `%artifact_name%` artifact in your build script: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| * To use runtime route annotations, add the `%artifact_name%` artifact in your build script: | |
| * To use runtime route annotations, add the `%artifact_name%` artifact to your build script: |
|
|
||
| ## Generate and serve the specification | ||
|
|
||
| The OpenAPI specification is assembled at runtime using metadata generated by the compiler plugin together with runtime |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It reads a bit confusing, is "together with annotations" refers to the compiler plugin or specification?
maybe put it in a different order:
The OpenAPI specification is assembled at runtime from runtime route annotations and metadata generated by the compiler plugin.
| | `@externalDocs` | `@external href` | External documentation links | | ||
| ### Runtime route annotations {id="runtime-route-annotations"} | ||
|
|
||
| In cases where compile-time analysis is insufficient — such as when using dynamic routing, interceptors, or conditional |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| In cases where compile-time analysis is insufficient — such as when using dynamic routing, interceptors, or conditional | |
| In cases where compile-time analysis is insufficient, such as when using dynamic routing, interceptors, or conditional |
| The metadata is attached to the routing tree at runtime and is consumed by the OpenAPI and Swagger UI plugins. | ||
|
|
||
| To generate the OpenAPI specification, run the following Gradle task: | ||
| The `.annotate { }` DSL maps directly to the OpenAPI Specification. Property names and structure correspond to the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| The `.annotate { }` DSL maps directly to the OpenAPI Specification. Property names and structure correspond to the | |
| The `.annotate {}` DSL maps directly to the OpenAPI specification. Property names and structure correspond to the |
| > ContentType.Application.Json | ||
| > ) | ||
| >``` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| > | |
| {style="note"} | |
| This assumes that a JSON serializer (for example, `kotlinx.serialization`) is installed. | ||
|
|
||
| > If you want to make serialization explicit or avoid relying on `ContentNegotiation`, you can encode the document | ||
| > manually and respond with JSON: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| > manually and respond with JSON: | |
| > manually and respond with a JSON: | |
| > |
| @@ -1,66 +1,106 @@ | |||
| [//]: # (title: OpenAPI specification generation) | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment for the whole page: there's now a mix of imperative headers like "Configure..", "Annotate..", "Generate and serve.." and noun phrases like "Code inference", "Metadate precedence" on the second header level. Perhaps you can rephrase them or move to a lower level. For example, "Enable code inference" or move "Metadata precedence" inside "Generate and serve.." since it's also about assembling the final specification
KTOR-9165
KTOR-9193